﻿using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

using Outcoder.Data.Serialization;


namespace System.IO.IsolatedStorage
{
	public sealed class IsolatedStorageSettings :
		IDictionary<string, object>, IDictionary,
		ICollection<KeyValuePair<string, object>>, ICollection,
		IEnumerable<KeyValuePair<string, object>>, IEnumerable
	{
		static IsolatedStorageSettings applicationSettings;

		readonly IsolatedStorageFile container;
		readonly Dictionary<string, object> settings;

		const string localSettingsFileName = "__LocalSettings";

		public bool RaiseExceptionOnDeserializeError { get; set; }

		internal IsolatedStorageSettings(IsolatedStorageFile storageFile)
		{
			container = storageFile;

			if (!storageFile.FileExists(localSettingsFileName))
			{
				settings = new Dictionary<string, object>();
				return;
			}

			using (IsolatedStorageFileStream fileStream = storageFile.OpenFile(localSettingsFileName, FileMode.Open))
			{
				try
				{
					settings = SilverlightSerializer.Deserialize<Dictionary<string, object>>(fileStream);
				}
				catch (Exception ex)
				{
					Debug.WriteLine("Unable to deserialize settings." + ex.ToString());
					settings = new Dictionary<string, object>();

					if (RaiseExceptionOnDeserializeError)
					{
						throw;
					}
				}
			}
		}

		~IsolatedStorageSettings()
		{
			try
			{
				Save();
			}
			catch (ObjectDisposedException)
			{
				/* Ignore. */
			}
		}

		public static IsolatedStorageSettings ApplicationSettings
		{
			get
			{
				if (applicationSettings == null)
				{
					applicationSettings = new IsolatedStorageSettings(
						IsolatedStorageFile.GetUserStoreForApplication());
				}
				return applicationSettings;
			}
		}

		public int Count
		{
			get
			{
				return settings.Count;
			}
		}

		public ICollection Keys
		{
			get
			{
				return settings.Keys;
			}
		}

		public ICollection Values
		{
			get
			{
				return settings.Values;
			}
		}

		public object this[string key]
		{
			get
			{
				return settings[key];
			}
			set
			{
				settings[key] = value;
			}
		}

		public void Add(string key, object value)
		{
			settings.Add(key, value);
		}

		public void Clear()
		{
			settings.Clear();
		}

		public bool Contains(string key)
		{
			if (key == null)
			{
				throw new ArgumentNullException("key");
			}

			return settings.ContainsKey(key);
		}

		public bool Remove(string key)
		{
			return settings.Remove(key);
		}

		public void Save()
		{
			using (IsolatedStorageFileStream fileStream = container.CreateFile(localSettingsFileName))
			{
				SilverlightSerializer.Serialize(settings, fileStream);
			}
		}

		public bool TryGetValue<T>(string key, out T value)
		{
			object settingsValue;
			if (!settings.TryGetValue(key, out settingsValue))
			{
				value = default(T);
				return false;
			}
			value = (T)settingsValue;
			return true;
		}

		int ICollection<KeyValuePair<string, object>>.Count
		{
			get
			{
				return settings.Count;
			}
		}

		bool ICollection<KeyValuePair<string, object>>.IsReadOnly
		{
			get
			{
				return false;
			}
		}

		void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
		{
			settings.Add(item.Key, item.Value);
		}

		void ICollection<KeyValuePair<string, object>>.Clear()
		{
			settings.Clear();
		}

		bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
		{
			return settings.ContainsKey(item.Key);
		}

		void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
		{
			((ICollection<KeyValuePair<string, object>>)settings).CopyTo(array, arrayIndex);
		}

		bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
		{
			return settings.Remove(item.Key);
		}

		ICollection<string> IDictionary<string, object>.Keys
		{
			get
			{
				return settings.Keys;
			}
		}

		ICollection<object> IDictionary<string, object>.Values
		{
			get
			{
				return settings.Values;
			}
		}

		bool IDictionary<string, object>.ContainsKey(string key)
		{
			return settings.ContainsKey(key);
		}

		bool IDictionary<string, object>.TryGetValue(string key, out object value)
		{
			return settings.TryGetValue(key, out value);
		}

		string ExtractKey(object key)
		{
			if (key == null)
			{
				throw new ArgumentNullException("key");
			}
			return (key as string);
		}

		void IDictionary.Add(object key, object value)
		{
			string extractedKey = ExtractKey(key);
			if (extractedKey == null)
			{
				throw new ArgumentException("key");
			}

			settings.Add(extractedKey, value);
		}

		void IDictionary.Clear()
		{
			settings.Clear();
		}

		bool IDictionary.Contains(object key)
		{
			string skey = ExtractKey(key);
			if (skey == null)
			{
				return false;
			}
			return settings.ContainsKey(skey);
		}

		object IDictionary.this[object key]
		{
			get
			{
				string extractedKey = ExtractKey(key);
				return (extractedKey == null) ? null : settings[extractedKey];
			}
			set
			{
				string extractedKey = ExtractKey(key);
				if (extractedKey == null)
				{
					throw new ArgumentException("key");
				}
				settings[extractedKey] = value;
			}
		}

		bool IDictionary.IsFixedSize
		{
			get
			{
				return false;
			}
		}

		bool IDictionary.IsReadOnly
		{
			get
			{
				return false;
			}
		}

		void IDictionary.Remove(object key)
		{
			string extractedKey = ExtractKey(key);
			if (extractedKey != null)
			{
				settings.Remove(extractedKey);
			}
		}

		void ICollection.CopyTo(Array array, int index)
		{
			((ICollection)settings).CopyTo(array, index);
		}

		bool ICollection.IsSynchronized
		{
			get
			{
				return ((ICollection)settings).IsSynchronized;
			}
		}

		object ICollection.SyncRoot
		{
			get
			{
				return ((ICollection)settings).SyncRoot;
			}
		}

		IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
		{
			return settings.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return settings.GetEnumerator();
		}

		IDictionaryEnumerator IDictionary.GetEnumerator()
		{
			return settings.GetEnumerator();
		}
	}
}
